This repository has no description
4.7 kB
171 lines
1'use client';
2
3import { useState, useEffect, useMemo, useCallback } from 'react';
4import { useRouter, useParams } from 'next/navigation';
5import {
6 Container,
7 Title,
8 TextInput,
9 Textarea,
10 Button,
11 Stack,
12 Card,
13 Group,
14 Alert,
15 LoadingOverlay,
16} from '@mantine/core';
17import { getAccessToken } from '@/services/auth';
18import { ApiClient } from '@/api-client/ApiClient';
19import type { GetCollectionPageResponse } from '@/api-client/types';
20
21export default function EditCollectionPage() {
22 const router = useRouter();
23 const params = useParams();
24 const collectionId = params.collectionId as string;
25
26 // Memoize API client to prevent recreation on every render
27 const apiClient = useMemo(
28 () =>
29 new ApiClient(
30 process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000',
31 () => getAccessToken(),
32 ),
33 [],
34 );
35
36 const [collection, setCollection] =
37 useState<GetCollectionPageResponse | null>(null);
38 const [name, setName] = useState('');
39 const [description, setDescription] = useState('');
40 const [isLoading, setIsLoading] = useState(true);
41 const [isSaving, setIsSaving] = useState(false);
42 const [error, setError] = useState<string | null>(null);
43 const [success, setSuccess] = useState(false);
44
45 // Memoize the load collection function to prevent recreation
46 const loadCollection = useCallback(async () => {
47 if (!collectionId) return;
48
49 try {
50 setIsLoading(true);
51 setError(null);
52 const response = await apiClient.getCollectionPage(collectionId);
53 setCollection(response);
54 setName(response.name);
55 setDescription(response.description || '');
56 } catch (err) {
57 setError('Failed to load collection');
58 console.error('Error loading collection:', err);
59 } finally {
60 setIsLoading(false);
61 }
62 }, [collectionId, apiClient]);
63
64 // Load collection data
65 useEffect(() => {
66 loadCollection();
67 }, [loadCollection]);
68
69 const handleSave = useCallback(async () => {
70 if (!name.trim()) {
71 setError('Collection name is required');
72 return;
73 }
74
75 try {
76 setIsSaving(true);
77 setError(null);
78
79 await apiClient.updateCollection({
80 collectionId,
81 name: name.trim(),
82 description: description.trim() || undefined,
83 });
84
85 setSuccess(true);
86
87 // Redirect back to collection page after a brief delay
88 setTimeout(() => {
89 router.push(`/collections/${collectionId}`);
90 }, 1500);
91 } catch (err) {
92 setError('Failed to update collection');
93 console.error('Error updating collection:', err);
94 } finally {
95 setIsSaving(false);
96 }
97 }, [name, description, collectionId, apiClient, router]);
98
99 const handleCancel = useCallback(() => {
100 router.push(`/collections/${collectionId}`);
101 }, [router, collectionId]);
102
103 if (isLoading) {
104 return (
105 <Container size="md" py="xl">
106 <LoadingOverlay visible />
107 </Container>
108 );
109 }
110
111 if (!collection) {
112 return (
113 <Container size="md" py="xl">
114 <Alert color="red" title="Collection not found" />
115 </Container>
116 );
117 }
118
119 return (
120 <Container size="md" py="xl">
121 <Stack gap="lg">
122 <Title order={1}>Edit Collection</Title>
123
124 <Card withBorder p="lg">
125 <Stack gap="md">
126 {error && <Alert color="red" title={error} />}
127
128 {success && (
129 <Alert color="green" title="Collection updated successfully! Redirecting..." />
130 )}
131
132 <TextInput
133 label="Collection Name"
134 placeholder="Enter collection name"
135 value={name}
136 onChange={(event) => setName(event.currentTarget.value)}
137 required
138 error={!name.trim() && error ? 'Name is required' : undefined}
139 />
140
141 <Textarea
142 label="Description"
143 placeholder="Enter collection description (optional)"
144 value={description}
145 onChange={(event) => setDescription(event.currentTarget.value)}
146 minRows={3}
147 maxRows={6}
148 />
149
150 <Group justify="flex-end" mt="md">
151 <Button
152 variant="subtle"
153 onClick={handleCancel}
154 disabled={isSaving}
155 >
156 Cancel
157 </Button>
158 <Button
159 onClick={handleSave}
160 loading={isSaving}
161 disabled={!name.trim() || success}
162 >
163 Save Changes
164 </Button>
165 </Group>
166 </Stack>
167 </Card>
168 </Stack>
169 </Container>
170 );
171}